package com.beiding.render;

import java.io.File;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;

public class ClasspathUtils {
    public static File getRootFile(Class clz) {
        try {

            URL url = clz.getResource("");
            String packageName = clz.getPackage().getName();

            String packageDirName = packageName.replace('.', '/');
            switch (url.getProtocol()) {
                case "file":
                    //截断找出文件夹
                    String path = new File(URLDecoder.decode(url.getFile())).getAbsolutePath();
                    packageDirName = ("/" + packageDirName).replace("/", File.separator);
                    path = path.substring(0, path.lastIndexOf(packageDirName));
                    return new File(path);
                case "jar":
                    JarURLConnection urlConnection = (JarURLConnection) url.openConnection();
                    //如果文件不存在,就将该jar文件放到临时路径下

                    URL jarFileURL = urlConnection.getJarFileURL();
                    return new File(URLDecoder.decode(jarFileURL.getFile()));
                default:
                    return null;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void deepFindClass(File root, File dir, Set<String> all) {
        String absolutePath = root.getAbsolutePath();
        File[] files = dir.listFiles();
        if (files != null) {
            for (File f : files) {
                if (f.isFile() && f.getName().endsWith(".class")) {

                    //确定类名
                    String substring = f.getAbsolutePath().substring(absolutePath.length());
                    String replace = substring.replace("/", ".").replace("\\", ".");
                    if (replace.startsWith(".")) {
                        replace = replace.substring(1);
                    }
                    replace = replace.substring(0, replace.lastIndexOf(".class"));
                    all.add(replace);
                } else if (f.isDirectory()) {
                    deepFindClass(root, f, all);
                }
            }
        }
    }


    //两种扫描方式
    public static Set<String> scanClassName(File file) {
        Set<String> scanResult = new HashSet<>();
        try {
            if (file.isDirectory()) {
                deepFindClass(file, file, scanResult);
            } else if (file.isFile()) {

                JarFile jarFile = new JarFile(file);
                jarFile.stream().forEach(o -> {
                    if (!o.isDirectory()) {
                        String name = o.getName();
                        if (name.endsWith(".class")) {
                            name = name.substring(0, name.length() - ".class".length()).replace("\\", ".").replace("/", ".");
                            scanResult.add(name);
                        }
                    }
                });

            }

        } catch (Throwable ignore) {
        }
        return scanResult;
    }


    public static Set<Class> scanClass(URLClassLoader classLoader, File file) {
        try {
            URL[] urLs = classLoader.getURLs();
            List<URL> urls = Arrays.asList(urLs);
            URL url = file.toURI().toURL();
            if (!urls.contains(url)) {
                Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                addURL.setAccessible(true);
                addURL.invoke(classLoader, url);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        Set<String> set = scanClassName(file);
        Set<Class> all = new HashSet<>();
        for (String s : set) {
            try {
                Class<?> aClass = classLoader.loadClass(s);
                all.add(aClass);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        return all;
    }

    public static Set<Class> scanClass(File file) {
        Set<String> set = scanClassName(file);
        Set<Class> all = new HashSet<>();
        for (String s : set) {
            try {
                Class<?> aClass = Class.forName(s);
                all.add(aClass);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        return all;
    }


}
